* Add rebuildImages.php to update image metadata fields
[lhc/web/wiklou.git] / maintenance / rebuildImages.php
1 <?php
2
3 require_once( 'commandLine.inc' );
4
5 class ImageBuilder {
6 function ImageBuilder() {
7 global $wgDatabase;
8
9 $this->dbw =& $this->newConnection();
10 $this->dbr =& $this->streamConnection();
11
12 $this->maxLag = 10; # if slaves are lagged more than 10 secs, wait
13 }
14
15 function build() {
16 $this->buildImage();
17 $this->buildOldImage();
18 }
19
20 /**
21 * Open a connection to the master server with the admin rights.
22 * @return Database
23 * @access private
24 */
25 function &newConnection() {
26 global $wgDBadminuser, $wgDBadminpassword;
27 global $wgDBserver, $wgDBname;
28 $db =& new Database( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );
29 return $db;
30 }
31
32 /**
33 * Open a second connection to the master server, with buffering off.
34 * This will let us stream large datasets in and write in chunks on the
35 * other end.
36 * @return Database
37 * @access private
38 */
39 function &streamConnection() {
40 $timeout = 3600 * 24;
41 $db =& $this->newConnection();
42 $db->bufferResults( false );
43 $db->query( "SET net_read_timeout=$timeout" );
44 $db->query( "SET net_write_timeout=$timeout" );
45 return $db;
46 }
47
48 /**
49 * Dump timestamp and message to output
50 * @param string $message
51 * @access private
52 */
53 function log( $message ) {
54 echo wfTimestamp( TS_DB ) . ': ' . $message . "\n";
55 flush();
56 }
57
58 function init( $count, $table ) {
59 $this->processed = 0;
60 $this->updated = 0;
61 $this->count = $count;
62 $this->startTime = wfTime();
63 $this->table = $table;
64 }
65
66 function progress( $updated ) {
67 $this->updated += $updated;
68 $this->processed++;
69 if( $this->processed % 100 != 0 ) {
70 return;
71 }
72 $portion = $this->processed / $this->count;
73 $updateRate = $this->updated / $this->processed;
74
75 $now = wfTime();
76 $delta = $now - $this->startTime;
77 $estimatedTotalTime = $delta / $portion;
78 $eta = $this->startTime + $estimatedTotalTime;
79
80 printf( "%s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec <%.2f%% updated>\n",
81 wfTimestamp( TS_DB, intval( $now ) ),
82 $portion * 100.0,
83 $this->table,
84 wfTimestamp( TS_DB, intval( $eta ) ),
85 $completed,
86 $this->count,
87 $rate,
88 $updateRate * 100.0 );
89 flush();
90 }
91
92 function buildTable( $table, $key, $callback ) {
93 $fname = 'ImageBuilder::buildTable';
94
95 $count = $this->dbw->selectField( $table, 'count(*)', '', $fname );
96 $this->init( $count, $table );
97 $this->log( "Processing $table..." );
98
99 $tableName = $this->dbr->tableName( $table );
100 $sql = "SELECT * FROM $tableName";
101 $result = $this->dbr->query( $sql, $fname );
102
103 while( $row = $this->dbr->fetchObject( $result ) ) {
104 $update = call_user_func( $callback, $row );
105 if( is_array( $update ) ) {
106 $this->dbw->update( $table,
107 $update,
108 array( $key => $row->$key ),
109 $fname );
110 $this->progress( 1 );
111 } else {
112 $this->progress( 0 );
113 }
114 }
115 $this->log( "Finished $table... $this->updated of $this->processed rows updated" );
116 $this->dbr->freeResult( $result );
117 }
118
119 function buildImage() {
120 $callback = array( &$this, 'imageCallback' );
121 $this->buildTable( 'image', 'img_name', $callback );
122 }
123
124 function imageCallback( $row ) {
125 if( $row->img_width ) {
126 // Already processed
127 return null;
128 }
129
130 // Fill in the new image info fields
131 $info = $this->imageInfo( $row->img_name );
132 return array(
133 'img_width' => $info['width'],
134 'img_height' => $info['height'],
135 'img_bits' => $info['bits'],
136 'img_media_type' => $info['media'],
137 'img_major_mime' => $info['major'],
138 'img_minor_mime' => $info['minor'] );
139 }
140
141
142 function buildOldImage() {
143 $this->buildTable( 'oldimage', 'oi_archive_name',
144 array( &$this, 'oldimageCallback' ) );
145 }
146
147 function oldimageCallback( $row ) {
148 if( $row->oi_width ) {
149 return null;
150 }
151
152 // Fill in the new image info fields
153 $info = $this->imageInfo( $row->oi_archive_name, 'wfImageArchiveDir', $row->oi_name );
154 return array(
155 'oi_width' => $info['width' ],
156 'oi_height' => $info['height'],
157 'oi_bits' => $info['bits' ] );
158 }
159
160 function imageInfo( $name, $subdirCallback='wfImageDir', $basename = null ) {
161 if( is_null( $basename ) ) $basename = $name;
162 $dir = call_user_func( $subdirCallback, $basename );
163 $filename = $dir . '/' . $name;
164 $info = array(
165 'width' => 0,
166 'height' => 0,
167 'bits' => 0,
168 'media' => '',
169 'major' => '',
170 'minor' => '' );
171
172 $magic =& wfGetMimeMagic();
173 $mime = $magic->guessMimeType( $filename, true );
174 list( $info['major'], $info['minor'] ) = explode( '/', $mime );
175
176 $info['media'] = $magic->getMediaType( $filename, $mime );
177
178 # Height and width
179 $gis = false;
180 if( $mime == 'image/svg' ) {
181 $gis = wfGetSVGsize( $this->imagePath );
182 } elseif( $magic->isPHPImageType( $mime ) ) {
183 $gis = getimagesize( $filename );
184 } else {
185 $this->log( "Surprising mime type: $mime" );
186 }
187 if( $gis ) {
188 $info['width' ] = $gis[0];
189 $info['height'] = $gis[1];
190 }
191 if( isset( $gis['bits'] ) ) {
192 $info['bits'] = $gis['bits'];
193 }
194
195 return $info;
196 }
197
198 }
199
200 $builder = new ImageBuilder();
201 $builder->build();
202
203 ?>